/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @file
 * @brief Interface of class Lexer(scanner) and token-stream.
 *
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#pragma once

#ifndef AMCC_LEXER_H
#define AMCC_LEXER_H

#include <stdio.h>
#include <stdbool.h>

#include "amcc_config.h"
#include "token.h"

AMCC_DECL_BEGIN


struct token_stream;
struct charStream;

/**
 * @brief Class lexer(scanner)
 *
 * @attention  All attributes SHOULD be accessed using API below
 *             named as "lexer_*()".
 */
typedef struct lexer {
// All member is PRIVATE , they SHOULD be accessed through API

    /// The token stream object, the product of a Lexer
    struct token_stream * tokenStream;
    int        nErrors;   //!< Count of lexical errors

// helper members for scan current file

    const char * inputFilePath;      //!< Path of input file
    FILE *       currentHandler;     //!< Handler of current input file
    struct charStream * charStream;  //!< The input buffer object

    t_position   currentPosition;    //!< Current position in current file
    EnumTokenKind  lastTokenKind;

} t_lexer, * t_lexer_ptr;


/**
 * @brief Class token-stream whose instance is the product of lexer.
 *
 * @attention  A token-stream is the product of lexer and can
 *        ONLY be used after the lexer is complete successfully.
 *
 * @attention  All attributes SHOULD be accessed using API below
 *             named as "tokenstream_*()".
 *
 * @note The kind of last token in a stream always is
 *       'TK_EOF' after the lexer works finished.
 */
typedef struct token_stream {
//private data represents all tokens

    /// Temporary storage for store tokens during lexical analysis.
    void *  tokens_buffer;

    /// Array of pointers to tokens.
    ///
    /// This array is easy/fast to access during syntax analysis,
    /// and is created immediately upon completion of lexical analysis.
    t_token_ptr * arrayTokens;

    /// The index of the first terminal(token) of current remaining input.
    ///
    /// This field is used to LOOK-AHEAD during syntax analysis.
    int       idxCurrentToken;
}  t_tokenstream ,  * t_tokenstream_ptr;


// -----------------------------------------------------------------------
/// @name API of class lexer
/// @{


/**
 * @brief Creates and initializes a new lexer.
 *
 * @param inputFilePath  path of input file
 * @return On success, return a pointer to a new lexer.
 * @return On failure, return null.
 */
extern t_lexer_ptr lexer_new(const char* inputFilePath);

/**
 * @brief Destroys this lexer and release inner resource.
 * @param pThis  pointer to this lexer
 * @note This function releases all resources used by lexer, and also
 *       destroys tokens if LEXER_GRAB_STREAM(pThis) is not called.
 */
extern void  lexer_destroy(t_lexer_ptr pThis);

/**
 * @brief  Performs lexical analysis, that is, scanning input file,
 *         recognizing tokens.
 *
 * All tokens are stored in a token-stream which
 * terminates with an EOF token.
 *
 * @param pThis  pointer to this lexer
 * @return Return 0 on success, and
 *         return negative value on failure.
 *
 * @attention Exceptions and behavior: <br>
 * - fail on open file: skip this file, stop work. <br>
 * - fail on memory allocation: stop work. <br>
 * - bad character:  discard it and more following chars immediately
 *            following it until a space or EOL is encountered.
 */
extern int lexer_run(t_lexer_ptr pThis);

/**
 * @brief  Get the number of lexical errors and failures on file I/O.
 * @param pThis  pointer to this parser
 * @return the number of lexical errors.
 */
extern int lexer_error_count(t_lexer_ptr pThis);

/**
 * @brief Dump tokens into o a file which path is defined by
 *        amcc_get_tokenFilePath().
 *
 * @param pThis pointer to this Lexer
 *
 * @note This function is equivalent to tokenstream_dump().
 * @see amcc_get_tokenFilePath()
 */
extern void lexer_dump_tokens(t_lexer_ptr pThis);

/**
 * @brief Get the token stream that is the product of lexer.
 * @param pThis pointer to this lexer
 * @return Pointer to the token stream.
 * @warning Caller DO NOT destroy the returned stream. This stream
 *  will be destroyed by Lexer self.
 */
extern t_tokenstream_ptr lexer_get_tokenstream(t_lexer_ptr pThis);

/**
 * @brief TRANSFER the ownership of the token stream to caller.
 *
 * @param pThis pointer to this lexer
 * @return a pointer to the token stream.
 * @warning Caller MUST destroy the returned stream after
 *          call this function.
 */
extern t_tokenstream_ptr lexer_GRAB_tokenstream(t_lexer_ptr pThis);

/**
 * @brief get the number of tokens at now
 * @param pThis  pointer to this lexer
 * @return the number of tokens at now
 */
extern int lexer_tokens_count(t_lexer_ptr pThis);

/// @}
// -----------------------------------------------------------------------


// -----------------------------------------------------------------------
/// @name API of class token-stream
/// @{


/**
 * @brief Whether this stream is empty.
 * @param pThis  pointer to this stream
 * @return return true if this stream is empty or ONLY has an EOF token,
 *    otherwise return false(stream has one or more non-EOF tokens).
 */
extern bool tokenstream_isEmpty(t_tokenstream_ptr pThis);

/**
 * @brief Get the quantity of tokens in stream.
 * @param pThis  pointer to this stream
 * @return return the quantity of tokens in stream,
 *   this number including the EOF token.
 */
extern int tokenstream_length(t_tokenstream_ptr pThis);

/**
 * @brief Get index of last consumed element.
 * @param pThis  pointer to this stream
 * @return the index of last consumed element.
 * @return -1 means no any token has consumed.
 */
extern int tokenstream_currentIndex(t_tokenstream_ptr pThis);

/**
 * @brief Reset current index to first element.
 * @param pThis  pointer to this stream
 */
extern void tokenstream_resetIndex(t_tokenstream_ptr pThis);

/**
 * @brief Peek the kind of next Kth token following current token.
 * @param pThis  pointer to this stream
 * @param K      offset
 * @return Return the kind of next Kth token.
 *  If the next Kth token does not exist, return 'TK_EOF'.
 *
 * @note This function does not modify any inner data of stream.
 */
extern EnumTokenKind tokenstream_lookAhead(t_tokenstream_ptr pThis, int K);

/**
 * @brief Peek the next Kth token following current token.
 * @param pThis  pointer to this stream
 * @param K      offset
 * @return Return a pointer to the next Kth token.
 *  If the next Kth token does not exist, return a token for 'TK_EOF'.
 *
 * @note In any case, this function does not return null.
 * @note This function does not modify any inner data of stream.
 */
extern t_token_ptr tokenstream_lookAheadToken(t_tokenstream_ptr pThis, int K);

/**
 * @brief Consume current token.
 * <p>
 * This function means call nextToken(stream), and then
 * call lookAheadToken(stream, 1).
 *
 * @param pThis pointer to this stream
 * @return Return a pointer to the next token.
 */
extern t_token_ptr tokenstream_consume(t_tokenstream_ptr pThis);


/**
 * @brief Fetch next token from this stream.
 * @param pThis  pointer to this stream
 * @return Return next token and mark it as current token.
 *
 * @note This function will change inner data of stream
 *       when EOF is not fetched.
 * @note After EOF is fetched, this function always
 *       return current EOF token.
 */
extern t_token_ptr tokenstream_nextToken(t_tokenstream_ptr pThis);


/**
 * @brief Destroy a token stream, all tokens in stream will also be destroyed.
 *
 * @param pThis    pointer to this stream
 */
extern void tokenstream_destroy(t_tokenstream_ptr pThis);

/**
 * @brief Dump tokens into o a file which path is defined by
 *        amcc_get_tokenFilePath().
 *
 * @param pThis pointer to this stream
 *
 * @see amcc_get_tokenFilePath()
 */
extern void tokenstream_dump(t_tokenstream_ptr pThis);


/// Look Ahead the kind of next Kth token
#define TOKENSTREAM_LA(pStream, k)      tokenstream_lookAhead(pStream, k)

/// Look Ahead the kind of next token
#define TOKENSTREAM_LA1(pStream)        tokenstream_lookAhead(pStream, 1)

/// Peek the next Kth token
#define TOKENSTREAM_LT(pStream, k)      tokenstream_lookAheadToken(pStream, k)

/// Peek the next token
#define TOKENSTREAM_LT1(pStream)        tokenstream_lookAheadToken(pStream, 1)

/// @}
// -----------------------------------------------------------------------


AMCC_DECL_END

#endif // #ifndef AMCC_LEXER_H
